/**
    @author: dt
    @date: 2025-01-23
    @description: 定点数实现，使用64位存储，24位小数部分
*/

using System;
using UnityEngine;

namespace DLib.FPMath
{
    /// <summary>
    /// 定点数类型，使用64位存储，24位小数部分
    /// 整数部分：40位（-2^39 到 2^39-1）
    /// 小数部分：24位（精度约为 1/16777216）
    /// </summary>
    [Serializable]
    public struct FPNumber : IEquatable<FPNumber>, IComparable<FPNumber>
    {
        // 定点数常量
        public const int FRACTIONAL_BITS = 24;
        public const long FRACTIONAL_MASK = (1L << FRACTIONAL_BITS) - 1;
        public const long INTEGER_MASK = ~FRACTIONAL_MASK;
        public const long ONE = 1L << FRACTIONAL_BITS;
        public const long HALF = ONE >> 1;
        public const long PI = (long)(3.14159265358979323846 * ONE);
        public const long PI_OVER_2 = PI >> 1;
        public const long PI_OVER_4 = PI >> 2;
        public const long TWO_PI = PI << 1;
        public const long DEG_TO_RAD = PI / 180;
        public const long RAD_TO_DEG = 180 * ONE / PI;

        // 内部存储
        private long rawValue;

        #region 构造函数

        public FPNumber(long rawValue)
        {
            this.rawValue = rawValue;
        }

        public FPNumber(int value)
        {
            this.rawValue = (long)value << FRACTIONAL_BITS;
        }

        public FPNumber(float value)
        {
            this.rawValue = (long)(value * ONE);
        }

        public FPNumber(double value)
        {
            this.rawValue = (long)(value * ONE);
        }

        #endregion

        #region 静态工厂方法

        public static FPNumber FromRaw(long rawValue)
        {
            return new FPNumber(rawValue);
        }

        public static FPNumber FromInt(int value)
        {
            return new FPNumber(value);
        }

        public static FPNumber FromFloat(float value)
        {
            return new FPNumber(value);
        }

        public static FPNumber FromDouble(double value)
        {
            return new FPNumber(value);
        }

        public static FPNumber Zero => new FPNumber(0);
        public static FPNumber One => new FPNumber(ONE);
        public static FPNumber Half => new FPNumber(HALF);
        public static FPNumber Pi => new FPNumber(PI);
        public static FPNumber PiOver2 => new FPNumber(PI_OVER_2);
        public static FPNumber PiOver4 => new FPNumber(PI_OVER_4);
        public static FPNumber TwoPi => new FPNumber(TWO_PI);
        public static FPNumber DegToRad => new FPNumber(DEG_TO_RAD);
        public static FPNumber RadToDeg => new FPNumber(RAD_TO_DEG);

        #endregion

        #region 属性

        public long RawValue => rawValue;

        public int IntegerPart => (int)(rawValue >> FRACTIONAL_BITS);

        public long FractionalPart => rawValue & FRACTIONAL_MASK;

        public float AsFloat => (float)rawValue / ONE;

        public double AsDouble => (double)rawValue / ONE;

        public int AsInt => (int)(rawValue >> FRACTIONAL_BITS);

        public bool IsZero => rawValue == 0;

        public bool IsPositive => rawValue > 0;

        public bool IsNegative => rawValue < 0;

        #endregion

        #region 基本运算

        public static FPNumber operator +(FPNumber a, FPNumber b)
        {
            return new FPNumber(a.rawValue + b.rawValue);
        }

        public static FPNumber operator -(FPNumber a, FPNumber b)
        {
            return new FPNumber(a.rawValue - b.rawValue);
        }

        public static FPNumber operator *(FPNumber a, FPNumber b)
        {
            long result = (a.rawValue * b.rawValue) >> FRACTIONAL_BITS;
            return new FPNumber(result);
        }

        public static FPNumber operator /(FPNumber a, FPNumber b)
        {
            if (b.rawValue == 0)
                throw new DivideByZeroException();
            
            long result = (a.rawValue << FRACTIONAL_BITS) / b.rawValue;
            return new FPNumber(result);
        }

        public static FPNumber operator -(FPNumber a)
        {
            return new FPNumber(-a.rawValue);
        }

        public static FPNumber operator ++(FPNumber a)
        {
            return new FPNumber(a.rawValue + ONE);
        }

        public static FPNumber operator --(FPNumber a)
        {
            return new FPNumber(a.rawValue - ONE);
        }

        #endregion

        #region 比较运算

        public static bool operator ==(FPNumber a, FPNumber b)
        {
            return a.rawValue == b.rawValue;
        }

        public static bool operator !=(FPNumber a, FPNumber b)
        {
            return a.rawValue != b.rawValue;
        }

        public static bool operator <(FPNumber a, FPNumber b)
        {
            return a.rawValue < b.rawValue;
        }

        public static bool operator >(FPNumber a, FPNumber b)
        {
            return a.rawValue > b.rawValue;
        }

        public static bool operator <=(FPNumber a, FPNumber b)
        {
            return a.rawValue <= b.rawValue;
        }

        public static bool operator >=(FPNumber a, FPNumber b)
        {
            return a.rawValue >= b.rawValue;
        }

        #endregion

        #region 数学函数

        /// <summary>
        /// 最小值
        /// </summary>
        public static FPNumber Min(FPNumber a, FPNumber b)
        {
            return a < b ? a : b;
        }

        /// <summary>
        /// 最大值
        /// </summary>
        public static FPNumber Max(FPNumber a, FPNumber b)
        {
            return a > b ? a : b;
        }

        /// <summary>
        /// 绝对值
        /// </summary>
        public static FPNumber Abs(FPNumber value)
        {
            return new FPNumber(Math.Abs(value.rawValue));
        }

        /// <summary>
        /// 符号函数
        /// </summary>
        public static FPNumber Sign(FPNumber value)
        {
            if (value.rawValue == 0) return Zero;
            return value.rawValue > 0 ? One : -One;
        }

        /// <summary>
        /// 平方
        /// </summary>
        public static FPNumber Square(FPNumber value)
        {
            return value * value;
        }

        /// <summary>
        /// 平方根（使用牛顿迭代法）
        /// </summary>
        public static FPNumber Sqrt(FPNumber value)
        {
            if (value.rawValue <= 0) return Zero;
            if (value.rawValue == ONE) return One;

            long x = value.rawValue;
            long y = (x + ONE) >> 1; // 初始猜测

            // 牛顿迭代
            for (int i = 0; i < 8; i++)
            {
                y = (y + (x << FRACTIONAL_BITS) / y) >> 1;
            }

            return new FPNumber(y);
        }

        /// <summary>
        /// 立方根
        /// </summary>
        public static FPNumber Cbrt(FPNumber value)
        {
            if (value.rawValue == 0) return Zero;
            if (value.rawValue == ONE) return One;

            long x = Math.Abs(value.rawValue);
            long y = x / 3; // 初始猜测

            // 牛顿迭代
            for (int i = 0; i < 8; i++)
            {
                long y2 = (y * y) >> FRACTIONAL_BITS;
                long y3 = (y2 * y) >> FRACTIONAL_BITS;
                y = ((2 * y3 + x) * y) / (3 * y3);
            }

            return new FPNumber(value.rawValue < 0 ? -y : y);
        }

        /// <summary>
        /// 幂运算
        /// </summary>
        public static FPNumber Pow(FPNumber baseValue, int exponent)
        {
            if (exponent == 0) return One;
            if (exponent == 1) return baseValue;
            if (exponent == 2) return Square(baseValue);

            bool negative = exponent < 0;
            exponent = Math.Abs(exponent);

            FPNumber result = One;
            FPNumber power = baseValue;

            while (exponent > 0)
            {
                if ((exponent & 1) != 0)
                {
                    result *= power;
                }
                power *= power;
                exponent >>= 1;
            }

            return negative ? One / result : result;
        }

        /// <summary>
        /// 自然对数（使用泰勒级数近似）
        /// </summary>
        public static FPNumber Log(FPNumber value)
        {
            if (value.rawValue <= 0) throw new ArgumentException("Log of non-positive number");

            // 将值归一化到 [1, 2) 区间
            long x = value.rawValue;
            int shift = 0;
            
            while (x >= (2L << FRACTIONAL_BITS))
            {
                x >>= 1;
                shift++;
            }
            while (x < ONE)
            {
                x <<= 1;
                shift--;
            }

            // 使用泰勒级数计算 ln(x)
            FPNumber z = new FPNumber(x - ONE) / new FPNumber(x + ONE);
            FPNumber z2 = z * z;
            FPNumber result = z;
            FPNumber term = z;

            for (int i = 3; i <= 15; i += 2)
            {
                term *= z2;
                result += term / i;
            }

            result *= 2;
            result += shift * Log(2);

            return result;
        }

        /// <summary>
        /// 指数函数
        /// </summary>
        public static FPNumber Exp(FPNumber value)
        {
            // 使用 e^x = (1 + x/n)^n 的近似
            const int n = 16;
            FPNumber x = value / n;
            FPNumber result = One + x;

            for (int i = 1; i < n; i++)
            {
                result *= (One + x);
            }

            return result;
        }

        #endregion

        #region 三角函数

        /// <summary>
        /// 正弦函数（使用查找表近似）
        /// </summary>
        public static FPNumber Sin(FPNumber angle)
        {
            // 将角度归一化到 [0, 2π)
            long normalized = angle.rawValue % TWO_PI;
            if (normalized < 0) normalized += TWO_PI;

            // 使用查找表
            return SinLookup(normalized);
        }

        /// <summary>
        /// 余弦函数
        /// </summary>
        public static FPNumber Cos(FPNumber angle)
        {
            return Sin(angle + PiOver2);
        }

        /// <summary>
        /// 正切函数
        /// </summary>
        public static FPNumber Tan(FPNumber angle)
        {
            FPNumber cos = Cos(angle);
            if (cos.IsZero) throw new ArithmeticException("Tangent undefined");
            return Sin(angle) / cos;
        }

        /// <summary>
        /// 反正弦函数
        /// </summary>
        public static FPNumber Asin(FPNumber value)
        {
            if (value.rawValue < -ONE || value.rawValue > ONE)
                throw new ArgumentException("Asin input out of range");

            // 使用泰勒级数近似
            FPNumber x = value;
            FPNumber result = x;
            FPNumber term = x;
            FPNumber x2 = x * x;

            for (int i = 3; i <= 15; i += 2)
            {
                term *= x2 * (i - 2) / i;
                result += term;
            }

            return result;
        }

        /// <summary>
        /// 反余弦函数
        /// </summary>
        public static FPNumber Acos(FPNumber value)
        {
            return PiOver2 - Asin(value);
        }

        /// <summary>
        /// 反正切函数
        /// </summary>
        public static FPNumber Atan(FPNumber value)
        {
            // 使用查找表近似
            return AtanLookup(value.rawValue);
        }

        /// <summary>
        /// 反正切2函数
        /// </summary>
        public static FPNumber Atan2(FPNumber y, FPNumber x)
        {
            if (x.IsZero && y.IsZero) return Zero;

            FPNumber angle = Atan(y / x);

            if (x.rawValue < 0)
            {
                angle = y.rawValue >= 0 ? angle + Pi : angle - Pi;
            }

            return angle;
        }

        #endregion

        #region 插值函数

        /// <summary>
        /// 线性插值
        /// </summary>
        public static FPNumber Lerp(FPNumber a, FPNumber b, FPNumber t)
        {
            return a + (b - a) * t;
        }

        /// <summary>
        /// 平滑插值（使用三次函数）
        /// </summary>
        public static FPNumber SmoothLerp(FPNumber a, FPNumber b, FPNumber t)
        {
            FPNumber t2 = t * t;
            FPNumber t3 = t2 * t;
            FPNumber smoothT = 3 * t2 - 2 * t3;
            return Lerp(a, b, smoothT);
        }

        /// <summary>
        /// 球形插值
        /// </summary>
        public static FPNumber SphericalLerp(FPNumber a, FPNumber b, FPNumber t)
        {
            FPNumber delta = b - a;
            
            // 处理角度环绕
            if (delta.rawValue > PI) delta -= TwoPi;
            if (delta.rawValue < -PI) delta += TwoPi;

            return a + delta * t;
        }

        #endregion

        #region 查找表

        private static readonly FPNumber[] sinTable = new FPNumber[1024];
        private static readonly FPNumber[] atanTable = new FPNumber[1024];

        static FPNumber()
        {
            InitializeLookupTables();
        }

        private static void InitializeLookupTables()
        {
            // 初始化正弦查找表
            for (int i = 0; i < 1024; i++)
            {
                double angle = (i * Math.PI * 2) / 1024.0;
                sinTable[i] = new FPNumber(Math.Sin(angle));
            }

            // 初始化反正切查找表
            for (int i = 0; i < 1024; i++)
            {
                double x = (i - 512) / 512.0;
                atanTable[i] = new FPNumber(Math.Atan(x));
            }
        }

        private static FPNumber SinLookup(long angle)
        {
            // 将角度映射到 [0, 1024) 区间
            int index = (int)((angle * 1024) / TWO_PI) & 0x3FF;
            return sinTable[index];
        }

        private static FPNumber AtanLookup(long value)
        {
            // 将值映射到查找表索引
            int index = (int)((value * 512) / ONE) + 512;
            index = Math.Max(0, Math.Min(1023, index));
            return atanTable[index];
        }

        #endregion

        #region 类型转换

        public static implicit operator FPNumber(int value) => new FPNumber(value);
        public static implicit operator FPNumber(float value) => new FPNumber(value);
        public static implicit operator FPNumber(double value) => new FPNumber(value);

        public static explicit operator int(FPNumber value) => value.AsInt;
        public static explicit operator float(FPNumber value) => value.AsFloat;
        public static explicit operator double(FPNumber value) => value.AsDouble;

        #endregion

        #region IEquatable, IComparable 实现

        public bool Equals(FPNumber other)
        {
            return rawValue == other.rawValue;
        }

        public int CompareTo(FPNumber other)
        {
            return rawValue.CompareTo(other.rawValue);
        }

        public override bool Equals(object obj)
        {
            return obj is FPNumber other && Equals(other);
        }

        public override int GetHashCode()
        {
            return rawValue.GetHashCode();
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return AsFloat.ToString("F6");
        }

        public string ToString(string format)
        {
            return AsFloat.ToString(format);
        }

        #endregion
    }
} 